/*
 * ========THE SOLMIX PROJECT=====================================
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.gnu.org/licenses/ 
 * or see the FSF site: http://www.fsf.org. 
 */
package org.solmix.web.servlets;

import java.util.Map;

import javax.servlet.Filter;

import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.util.CollectionUtils;
import org.apache.shiro.util.Nameable;
import org.apache.shiro.util.StringUtils;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.filter.authc.AuthenticationFilter;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
import org.apache.shiro.web.filter.mgt.FilterChainManager;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.apache.shiro.web.mgt.WebSecurityManager;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.solmix.services.exception.SLXException;
import org.solmix.services.types.Texception;
import org.solmix.services.types.Tmodule;
import org.solmix.web.security.ShiroConfigIniManager;


/**
 * 
 * @author solomon
 * @version $Id$  2011-6-5
 */

public class SecurityFilter extends AbstractShiroFilter
{
   private static transient final Logger log = LoggerFactory.getLogger(SecurityFilter.class);
   @Override
   public void init() throws Exception {
      configure();
   }

private ShiroConfigIniManager shiro;
   /**
    * @throws SLXException 
    * 
    */
   private void configure() throws SLXException
   {
      log.debug("creating shiro filter instance.");
//       shiro=SLXServices.getShiroIni();
      SecurityManager securityManager = shiro.getSecurityManager();
      if(securityManager==null){
         String __msg = "SecurityManager property must be set.";
         throw new SLXException(Tmodule.SERVLET,Texception.OSGI_BULEPRINT_INI_FAILE,__msg);
      }
      if (!(securityManager instanceof WebSecurityManager)) {
         String __msg = "The security manager does not implement the WebSecurityManager interface.";
         throw new SLXException(Tmodule.SERVLET,Texception.OBJECT_TYPE_NOT_ADAPTED,__msg);
      }
      FilterChainManager manager = createFilterChainManager();

      //Expose the constructed FilterChainManager by first wrapping it in a
      // FilterChainResolver implementation. The AbstractShiroFilter implementations
      // do not know about FilterChainManagers - only resolvers:
      PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
      chainResolver.setFilterChainManager(manager);
      setSecurityManager((WebSecurityManager)securityManager);
          setFilterChainResolver(chainResolver);
   }
   protected FilterChainManager createFilterChainManager() {

      DefaultFilterChainManager manager = new DefaultFilterChainManager();
      Map<String, Filter> defaultFilters = manager.getFilters();
      //apply global settings if necessary:
      for (Filter filter : defaultFilters.values()) {
          applyGlobalPropertiesIfNecessary(filter);
      }

      //Apply the acquired and/or configured filters:
      Map<String, Filter> filters = shiro.getFilters();
      if (!CollectionUtils.isEmpty(filters)) {
          for (Map.Entry<String, Filter> entry : filters.entrySet()) {
              String name = entry.getKey();
              Filter filter = entry.getValue();
              applyGlobalPropertiesIfNecessary(filter);
              if (filter instanceof Nameable) {
                  ((Nameable) filter).setName(name);
              }
              manager.addFilter(name, filter, true);
          }
      }

      //build up the chains:
      Map<String, String> chains = shiro.getFilterChainDefinitionMap();
      if (!CollectionUtils.isEmpty(chains)) {
          for (Map.Entry<String, String> entry : chains.entrySet()) {
              String url = entry.getKey();
              String chainDefinition = entry.getValue();
              manager.createChain(url, chainDefinition);
          }
      }

      return manager;
  }
   private void applyGlobalPropertiesIfNecessary(Filter filter) {
      applyLoginUrlIfNecessary(filter);
      applySuccessUrlIfNecessary(filter);
      applyUnauthorizedUrlIfNecessary(filter);
  }
   private void applyLoginUrlIfNecessary(Filter filter) {
      String loginUrl = shiro.getLoginUrl();
      if (StringUtils.hasText(loginUrl) && (filter instanceof AccessControlFilter)) {
          AccessControlFilter acFilter = (AccessControlFilter) filter;
          //only apply the login url if they haven't explicitly configured one already:
          String existingLoginUrl = acFilter.getLoginUrl();
          if (AccessControlFilter.DEFAULT_LOGIN_URL.equals(existingLoginUrl)) {
              acFilter.setLoginUrl(loginUrl);
          }
      }
  }

  private void applySuccessUrlIfNecessary(Filter filter) {
      String successUrl = shiro.getSuccessUrl();
      if (StringUtils.hasText(successUrl) && (filter instanceof AuthenticationFilter)) {
          AuthenticationFilter authcFilter = (AuthenticationFilter) filter;
          //only apply the successUrl if they haven't explicitly configured one already:
          String existingSuccessUrl = authcFilter.getSuccessUrl();
          if (AuthenticationFilter.DEFAULT_SUCCESS_URL.equals(existingSuccessUrl)) {
              authcFilter.setSuccessUrl(successUrl);
          }
      }
  }

  private void applyUnauthorizedUrlIfNecessary(Filter filter) {
      String unauthorizedUrl = shiro.getUnauthorizedUrl();
      if (StringUtils.hasText(unauthorizedUrl) && (filter instanceof AuthorizationFilter)) {
          AuthorizationFilter authzFilter = (AuthorizationFilter) filter;
          //only apply the unauthorizedUrl if they haven't explicitly configured one already:
          String existingUnauthorizedUrl = authzFilter.getUnauthorizedUrl();
          if (existingUnauthorizedUrl == null) {
              authzFilter.setUnauthorizedUrl(unauthorizedUrl);
          }
      }
  }
}
